/*********************************************************************************
 *      Copyright:  (C) 2019 Wu Yujun<540726307@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  ppp_oper.c
 *    Description:  This file ppp operation
 *                 
 *        Version:  1.0.0(2019年07月08日)
 *         Author:  Wu Yujun <540726307@qq.com>
 *      ChangeLog:  1, Release initial version on "2019年07月08日 20时51分42秒"
 *                 
 ********************************************************************************/
#include "ppp.h"


/*  
 *  描述： 使用ifcongfig相对应的网卡，查看网卡是否使能
 *
 * */
int ifconfig_check_ppp0(void)
{
    char    check_cmd[128] ;
    char    check_file[64] ;
    int     fd = -1 ; 
    char    buf[256] ;
    int     rv = -1 ;

    snprintf(check_file, sizeof(check_file),"ppp0.txt") ;
    /*  将标准输出重定向到文件  */
    snprintf(check_cmd, sizeof(check_cmd), "ifconfig ppp0 >%s",check_file ) ; 
    system(check_cmd) ;
    fd = open(check_file, O_RDONLY) ;
    if(fd < 0)
    {
        printf("Open %s error:%s\n", check_file, strerror(errno)) ;
        rv = -2 ;
        goto cleanup ;
    }
    memset(buf, 0 , sizeof(buf)) ;
    rv = read(fd, buf, sizeof(buf)) ;
    if(rv < 0)
    {
        printf("Read from %s error: %s\n",check_file ,strerror(errno)) ;
        rv =  -3 ;
        goto cleanup ;
    }
    /*  读出来后删除文件防止资源浪费 */
    rv = remove(check_file) ;
    if(rv < 0)
    {
        printf("Remove %s fail:%s\n",check_file, strerror(errno));
        rv = -4 ;
        goto cleanup ;
    }
    if(strstr(buf, "P-t-P:"))
    {
        printf("PPP0 up\n") ;
        rv = PPP0_UP;
    }
    else
    {
        printf("PPP0 down\n") ;
        rv = PPP0_DOWN ;
    }

cleanup :
    return rv ;
}


/************************
 *描述：PPP拨号连接
 *
 *返回值：
 *       成功返回0，失败返回负数
 ***********************/
int ppp_connect(st_gsmRegsiter gsmRegInfo, const char *pppd_script, const char *conn_script)
{
   
    int         rv = -1 ;

    rv = set_apn(gsmRegInfo,pppd_script, conn_script) ;
    if(rv < 0)
    {
        goto cleanup ;
    }

    rv = system("pppd call wcdma&") ;
    if(rv == -1 )
    {
        printf("system(pppd call wcdma) failed:%s\n",strerror(errno)) ;
        goto cleanup ;
    }
    do{                         
        rv = ifconfig_check_ppp0() ;
        sleep(1) ;
    }while(rv != PPP0_UP) ;//循环直到ppp0网卡使能

    rv = system("route add default dev ppp0") ; //直接根据网卡设备设置默认网关
    //rv = system("route add default gw `ifconfig ppp0|grep P-t-P|awk '{print $3}'|cut -d: -f2`") ; 
    if(rv == -1)
    {
        printf("system(ifconfig ppp0) error\n") ;
        goto cleanup ;
    }

    rv = ping_check_net() ;
    if(rv < 0)
    {
        printf("Can't connect the net\n") ;
        goto cleanup ;
    }
    
    printf("PPP connect OK!\n");


    rv = 0 ;
cleanup:
        return rv ;


}/* End Of Main */




/*************************************
 *   描述： 根据不同运营商设置apn
 *
 *   返回值：
 *          成功返回0，失败返回负数
 ************************************/
int  set_apn(st_gsmRegsiter gsmRegInfo,const char * pppd_script,const char* conn_script)
{
    int     chat_conn_fd ; 
    int     pppd_fd ;
    char    rbuf[1024] ;
    int     rv = -1 ;
    char    *ptr = NULL ;
    int     nbyte = 0 ;
    int     mcc = 0 ;
    int     msn = 0 ;
    char    wcdma[128] ;
    char    cdma[128] ;
    char    gprs_egde[128] ;
    char    temp[512] ;
    char    user[512] ;
    int     num ;


    if(!conn_script||!pppd_script)
    {
        printf("invail paremeter in %s,Please input chat shell name\n", __FUNCTION__) ;
        rv = -1 ;
        goto cleanup ;
    }
    /* 读取并修改chat连接脚本修改apn和拨号号码 */
    chat_conn_fd = open(conn_script, O_RDWR) ; //chat连接脚本
    if(chat_conn_fd < 0)
    {
        printf("open %s error:%s\n", conn_script, strerror(errno)) ;
        rv = -1 ;
        goto cleanup ;
    }

    memset(rbuf,0,sizeof(rbuf)) ;
    rv = read(chat_conn_fd,rbuf,sizeof(rbuf)) ;
    if(rv < 0)
    {
        printf("Read from %s error:%s\n",conn_script, strerror(errno)) ;
        rv = -2 ;
        goto cleanup ;
    }
    //printf("read %d byte:%s\n",rv,rbuf);
    ptr = strstr(rbuf,"AT+CGDCONT=1,") ;
    if(!ptr)
    {
        printf("can't fine AT+CGDCONT=1\n");
        rv = -3 ;
        goto cleanup ;
    }
    nbyte = ptr - rbuf ;
    lseek(chat_conn_fd, nbyte, SEEK_SET) ; //设置chat_conn文件偏移量
    
    mcc = atoi(gsmRegInfo.mcc_buf) ;
    msn = atoi(gsmRegInfo.msn_buf) ;

    /*  读取pppd脚本修改连接用户名和密码    */
    pppd_fd = open(pppd_script, O_RDWR) ; //pppd脚本
    if(pppd_fd < 0)
    {
        printf("open %s error:%s\n", pppd_script, strerror(errno)) ;
        rv = -1 ;
        goto cleanup ;
    }
    memset(rbuf,0,sizeof(rbuf)) ;
    rv = read(pppd_fd,rbuf,sizeof(rbuf)) ;
    if(rv < 0)
    {
        printf("Read from %s error:%s\n",conn_script, strerror(errno)) ;
        rv = -2 ;
        goto cleanup ;
    }
    //printf("read %d byte:%s\n",rv,rbuf);
    ptr = strstr(rbuf,"user") ;
    if(!ptr)
    {
        printf("can't fine user\n");
        rv = -3 ;
        goto cleanup ;
    }
    nbyte = ptr - rbuf ;
    num = rv - nbyte ; 
    lseek(pppd_fd, nbyte, SEEK_SET) ; //设置pppd脚本的文件偏移量从哪开始写
    /* user "wyj" 
     * password "wuyujun"    */
    ptr = strstr(rbuf,"password") ;
    if(!ptr)
    {
        printf("can't fine password\n");
        rv = -3 ;
        goto cleanup ;
    }
    ptr = strstr(ptr, "\"") ;
    ptr = ptr +1 ;
    ptr = strstr(ptr, "\"") ;
    ptr = ptr + 1 ;
    memset(temp,0,sizeof(temp)) ;
    strncpy(temp, ptr, rv - (ptr-rbuf) ) ; //将原本pppd脚本后面的保存下来

    if(mcc == Chinese_MCC)
    {
        switch(msn)
        {
            /* 中国移动 */
            case 0 :
            case 2 :
            case 4 :
            case 7 :
                memset(gprs_egde, 0, sizeof(gprs_egde)) ;
                snprintf(gprs_egde,sizeof(gprs_egde),
                        "AT+CGDCONT=1,\"IP\",\"cmnet\",,0,0\nOK-AT-OK ATDT*99***1#\nCONNECT \\d\\c\n") ;

                rv = write(chat_conn_fd ,gprs_egde,strlen(gprs_egde)) ;
                if(rv < 0)
                {
                    printf("write gprs_egde to %s error:%s\n",conn_script, strerror(errno)) ;
                    rv = -4 ;
                    goto cleanup ;
                }
                break ;
            /* 中国联通 */
            case 1 :
            case 6 :
            case 9 :
                 /* 写pppd脚本文件apn用户名和密码 */
                memset(user,0,sizeof(user)) ;
                snprintf(user, sizeof(user), "user \"wyj\"\npassword \"wuyujun\"") ;
                strncat(user,temp,sizeof(user)) ; //将用户名与密码与原本后面的内容拼接到一块
                //printf("User and Passwd:%s\n",user) ;
                rv = write(pppd_fd ,user,strlen(user)) ;
                if(rv < 0)
                {
                    printf("write wcdma to %s error:%s\n",pppd_script, strerror(errno)) ;
                    rv = -5 ;
                    goto cleanup ;
                }
                if(num > strlen(user)) //如果原本的内容比写入的内容长，将后面的注释掉
                {
                    rv = write(pppd_fd ,"#",strlen("#")) ;
                    if(rv < 0)
                    {
                        printf("write wcdma to %s error:%s\n",pppd_script, strerror(errno)) ;
                        rv = -5 ;
                        goto cleanup ;
                    }

                }
                /* 写chat连接脚本 */
                memset(wcdma,0,sizeof(wcdma)) ;
                snprintf(wcdma,sizeof(wcdma),
                        "AT+CGDCONT=1,\"IP\",\"3gnet\",,0,0\nOK-AT-OK ATDT*99#\nCONNECT \\d\\c\n") ;
                rv = write(chat_conn_fd ,wcdma,strlen(wcdma)) ;
                if(rv < 0)
                {
                    printf("write wcdma to %s error:%s\n",conn_script, strerror(errno)) ;
                    rv = -5 ;
                    goto cleanup ;
                }
                break ;
            /* 中国电信 */
            case 3 :
            case 5 :
                 /* 写pppd脚本文件apn用户名和密码 */
                memset(user,0,sizeof(user)) ;
                snprintf(user, sizeof(user), "user \"card\"\npassword \"card\"") ;
                strncat(user,temp,sizeof(user)) ;
                printf("User and Passwd:%s\n",user) ;
                rv = write(pppd_fd ,user,strlen(user)) ;
                if(rv < 0)
                {
                    printf("write wcdma to %s error:%s\n",pppd_script, strerror(errno)) ;
                    rv = -5 ;
                    goto cleanup ;
                }
                if(num > strlen(user)) //如果原本的内容比写入的内容长，将后面的注释掉
                {
                    rv = write(pppd_fd ,"#",strlen("#")) ;
                    if(rv < 0)
                    {
                        printf("write wcdma to %s error:%s\n",pppd_script, strerror(errno)) ;
                        rv = -5 ;
                        goto cleanup ;
                    }

                }
                /*  写chat连接脚本  */
                memset(cdma,0,sizeof(cdma)) ;
                snprintf(cdma,sizeof(cdma),
                        "AT+CGDCONT=1,\"IP\",,,0,0\nOK-AT-OK ATDT#777\nCONNECT \\d\\c\n") ;
                rv = write(chat_conn_fd ,cdma,strlen(cdma)) ;
                if(rv < 0)
                {
                    printf("write wcdma to %s error:%s\n",conn_script, strerror(errno)) ;
                    rv = -5 ;
                    goto cleanup ;
                }
                break ;
            default:

                break ;

        }
    }
    rv = 0 ;
cleanup:
    close(chat_conn_fd) ;
    return rv ;
}




/*********************************************** 
 *  描述：断开PPP拨号连接
 *
 *  参数：
 *      通过哪个AT命令串口设备发送ATH0来断开连接
 *  返回：
 *      成功返回0， 失败返回负数
 **********************************************/
int close_ppp(const char *name)
{

    st_comport      *comport = NULL;
    char            at_ath[16] ;
    char            rbuf[256] ;
    int             rv = -1 ;

    if(!name)
    {
        printf("Invail paremeter in %s\n", __FUNCTION__) ;
        rv = -1 ;
        goto cleanup ;
    }

    comport = comport_init(name, 115200,8,'N',1,'N') ;
    if(!comport)
    {
        printf("comport_init() failed\n") ;
        rv = -1 ;
        goto cleanup;
    }
    comport->fd = open_comport(comport) ;
    if(comport->fd < 0)
    {
        printf("Open_comport() Failed\n") ;
        rv = -2 ;
        goto cleanup ;
    }

    memset(at_ath,0,sizeof(at_ath)) ;
    snprintf(at_ath, sizeof(at_ath),"ATH0\r") ;
    rv = send_cmd(comport, at_ath, rbuf, sizeof(rbuf), 1600);
    if(rv < 0)
    {
        printf("ATH0 FAILED\n");
        rv = -3 ;
        goto cleanup;
    }
    printf("Read from gsm in %s:%s\n",__FUNCTION__, rbuf) ;

    rv = 0 ;
cleanup:
        comport_term(comport) ;
        return rv ;
}



/************************** 
 *  描述：测试网络连通性
 *
 *  返回值：
 *          成功返回0，,失败返回负数
 ******************************************/
int ping_check_net(void) 
{
    int     rv = -1 ;
    int     ping_log_fd = -1 ;
    char    buf[512] ;
    char        *ptr_start, *ptr_end ;
    char        rev_packet[8] ;
    int         rev_num = 0 ;



    rv = system("ping -c 3 -w 3 baidu.com >ping.log") ; //测试网络连通性
     if(rv == -1)
    {
        printf("system(ping) error\n") ;
        goto cleanup ;
    }
    sleep(3) ;
    ping_log_fd = open("ping.log", O_RDONLY) ;
    if(ping_log_fd < 0)
    {
        printf("open ping.log failed:%s\n", strerror(errno)) ;
        rv = -2 ;
        goto cleanup ;
    }
    lseek(ping_log_fd, 0, SEEK_SET) ;
    memset(buf, 0, sizeof(buf)) ;
    rv = read(ping_log_fd, buf, sizeof(buf)) ;
    if(rv < 0)
    {
        printf("read error:%s\n",strerror(errno));
        rv = -3 ;
        goto cleanup ;
    }

    //3 packets transmitted, 3 packets received, 0% packet loss
    ptr_start = strstr(buf,",") ;
    if((ptr_start == NULL))
    {
        rv = -4 ;
        goto cleanup ;
    }
    ptr_start = ptr_start + 1 ;
    ptr_end = strstr(ptr_start,"packets") ;
    
    memset(rev_packet, 0 , sizeof(rev_packet)) ;
    snprintf(rev_packet, ptr_end-ptr_start, "%s", ptr_start) ;
    rev_num = atoi(rev_packet) ;
    if(rev_num <= 0)
    {
        printf("No connect internet\n") ;
        rv = -5 ;
        goto cleanup ;
    }
    printf("Ping cmd OK!\n") ;

cleanup:
    return rv ;

}

